home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / plan / src / holiday.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  11KB  |  401 lines

  1. /*
  2.  * deals with the holiday file. A yacc parser is used to parse the file.
  3.  * All the holidays of the specified year are calculated at once and stored
  4.  * in two arrays that have one entry for each day of the year. The day
  5.  * drawing routines just use the julian date to index into these arrays.
  6.  * There are two arrays because holidays can be printed either on a full
  7.  * line under the day number, or as a small line to the right of the day
  8.  * number. It's convenient to have both.
  9.  *
  10.  *    parse_holidays(year, force)    read the holiday file and evaluate
  11.  *                    all the holiday definitions for
  12.  *                    <year>. Sets holiday and sm_holiday
  13.  *                    arrays. If force is set, re-eval even
  14.  *                    if year is the same as last time.
  15.  */
  16.  
  17. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <time.h>
  20. #include <Xm/Xm.h>
  21. #include "cal.h"
  22.  
  23.  
  24. /*
  25.  * Before you mail and complain that the following macro is incorrect,
  26.  * please consider that this is one of the main battlegrounds of the
  27.  * Annual Usenet Flame Wars. 2000 is a leap year. Just trust me on this :-)
  28.  */
  29.  
  30. #define LEAPYEAR(y)    !(y&3)
  31. #define JULIAN(m,d)    (monthbegin[m] + d-1 + (m>1 && LEAPYEAR(parse_year)))
  32. #define LAST        999
  33. #define ANY        0
  34. #define    BEFORE        -1
  35. #define AFTER        -2
  36.  
  37. extern time_t     date_to_time();
  38. extern char    *mystrdup();
  39. extern char    *resolve_tilde();    /* evaluates path with ~ */
  40. static int     calc_easter();
  41.  
  42. #if defined(bsdi) || defined(linux)
  43. int yylineno;
  44. #else
  45. extern int     yylineno;        /* current line # being parsed */
  46. #endif
  47. extern char    *yytext;        /* current token being parsed */
  48. extern FILE    *yyin;            /* the file the parser reads from */
  49. extern BOOL     yacc_small;        /* small string or on its own line? */
  50. extern int     yacc_stringcolor;    /* color of holiday name text, 1..8 */
  51. extern char    *yacc_string;        /* holiday name text */
  52. extern int     yacc_daycolor;        /* color of day number, 1..8 */
  53. extern char    *progname;        /* argv[0] */
  54. int         parse_year = -1;    /* year being parsed, 0=1970..99=2069*/
  55. static char    *filename;        /* holiday filename */
  56. static char     errormsg[200];        /* error message if any, or "" */
  57. static int     easter_julian;        /* julian date of Easter Sunday */
  58. static char    *holiday_name;        /* strdup'd yacc_string */
  59.  
  60. struct holiday     holiday[366];        /* info for each day, separate for */
  61. struct holiday     sm_holiday[366];    /* full-line texts under, and small */
  62.                     /* texts next to day number */
  63.  
  64. extern short    monthlen[12];
  65. extern short    monthbegin[12];
  66.  
  67.  
  68. static setliteraldate();
  69.  
  70. yyerror(msg) char *msg;
  71. {
  72.     fprintf(stderr, "%s: %s in line %d of %s\n", progname,
  73.                     msg, yylineno, HOLIDAY_PATH);
  74.     if (!*errormsg)
  75.         sprintf(errormsg,
  76.               "Problem with holiday file %s:\n%.80s in line %d",
  77.                     filename, msg, yylineno);
  78. }
  79.  
  80.  
  81.  
  82. /*
  83.  * parse the holiday text file, and set up the holiday arrays for a year.
  84.  * If year is -1, re-parse the last year parsed (this is used when the
  85.  * holiday file changes). If there is a CPP_PATH, check if the executable
  86.  * really exists, and if so, pipe the holioday files through it.
  87.  * Return an error message if an error occurred, 0 otherwise.
  88.  */
  89.  
  90. char *parse_holidays(year, force)
  91.     int            year;        /* year to parse for, 0=1970 */
  92.     BOOL            force;        /* file has changed, re-read */
  93. {
  94.     register struct holiday *hp;
  95.     register int        d, n;
  96.     char            *p, buf[200], cpp[200];
  97.     BOOL            piped = FALSE;
  98.  
  99.     if (year == parse_year && !force)
  100.         return(0);
  101.     if (year < 0)
  102.         year = parse_year;
  103.     parse_year = year;
  104.     easter_julian = calc_easter(year + 1900);
  105.  
  106.     for (hp=holiday, d=0; d < 366; d++, hp++)
  107.         if (hp->string) {
  108.             if (!hp->dup)
  109.                 free(hp->string);
  110.             hp->string      = 0;
  111.             hp->stringcolor = 0;
  112.             hp->daycolor    = 0;
  113.         }
  114.     for (hp=sm_holiday, d=0; d < 366; d++, hp++)
  115.         if (hp->string) {
  116.             if (!hp->dup)
  117.                 free(hp->string);
  118.             hp->string      = 0;
  119.             hp->stringcolor = 0;
  120.             hp->daycolor    = 0;
  121.         }
  122.  
  123.     for (n=0; n < 2; n++) {
  124.         sprintf(buf, "%s/%s", LIB, HOLIDAY_NAME);
  125.         filename = resolve_tilde(n ? buf : HOLIDAY_PATH);
  126.         if (access(filename, R_OK))
  127.             continue;
  128. #ifdef CPP_PATH
  129.         strncpy(cpp, CPP_PATH, sizeof(cpp));
  130.         cpp[sizeof(cpp)-1] = 0;
  131.         if (p = strchr(cpp, ' '))
  132.             *p = 0;
  133.         if (piped = !access(cpp, X_OK)) {
  134.             char cmd[200];
  135.             sprintf(cmd, "%s %s", CPP_PATH, filename);
  136.             yyin = popen(cmd, "r");
  137.         } else
  138. #endif
  139.             yyin = fopen(filename, "r");
  140.         if (!yyin)
  141.             continue;
  142.         *errormsg = 0;
  143.         yylineno = 0;
  144.         yyparse();
  145.         if (piped)
  146.             pclose(yyin);
  147.         else
  148.             fclose(yyin);
  149.         if (*errormsg)
  150.             return(errormsg);
  151.     }
  152.     return(0);
  153. }
  154.  
  155.  
  156. /*--------------------- yacc callbacks --------------------------------------*/
  157. /*
  158.  * set holiday by weekday (monday..sunday). The expression is
  159.  * "every <num>-th <wday> of <month> plus <off> days". num and month
  160.  * can be ANY or LAST.
  161.  */
  162.  
  163. setwday(num, wday, month, off, length)
  164.     int        num;        /* which, 1st..5th, ANY, or LAST */
  165.     int        wday;        /* 1=monday..7=sunday */
  166.     int        month;        /* month, 1..12, ANY, or LAST */
  167.     int        off;        /* offset in days */
  168.     int        length;        /* length in days */
  169. {
  170.     int        min_month = 0, max_month = 11;
  171.     int        min_num   = 0, max_num   = 4;
  172.     int        m, n, d, l, mlen, wday1;
  173.     int        dup = 0;
  174.  
  175.     if (month != ANY)
  176.         min_month = max_month = month-1;
  177.     if (month == LAST)
  178.         min_month = max_month = 11;
  179.     if (num != ANY)
  180.         min_num = max_num = num-1;
  181.  
  182.     holiday_name = yacc_string;
  183.     for (m=min_month; m <= max_month; m++) {
  184.         (void)date_to_time(1, m, parse_year, &wday1, 0, 0);
  185.         d = (wday-1 - (wday1-1) +7) % 7 + 1;
  186.         mlen = monthlen[m] + (m==1 && LEAPYEAR(parse_year));
  187.         if (num == LAST)
  188.             for (l=0; l < length; l++)
  189.                 setliteraldate(m, d+28<=mlen ? d+28 : d+21,
  190.                                 off+l, dup++);
  191.         else
  192.             for (d+=min_num*7, n=min_num; n <= max_num; n++, d+=7)
  193.                 if (d >= 1 && d <= mlen)
  194.                     for (l=0; l < length; l++)
  195.                         setliteraldate(m, d, off+l,
  196.                                     dup++);
  197.     }
  198. }
  199.  
  200.  
  201. /*
  202.  * set holiday by weekday (monday..sunday) date offset. The expression is
  203.  * "every <wday> before/after <date> plus <off> days". 
  204.  * (This routine contributed by Peter Littlefield <plittle@sofkin.ca>)
  205.  */
  206.  
  207. setdoff(wday, rel, month, day, year, off, length)
  208.     int        rel;        /* -1=before, -2=after */
  209.     int        wday;        /* 1=monday..7=sunday */
  210.     int        month;        /* month, 1..12, ANY, or LAST */
  211.     int        day;        /* 1..31, ANY, or LAST */
  212.     int        year;        /* 1..2069, or ANY */
  213.     int        off;        /* offset in days */
  214.     int        length;        /* length in days */
  215. {
  216.     int        min_month = 0, max_month = 11;
  217.     int        min_day   = 1, max_day   = 31;
  218.     int        m, d, nd, l, wday1;
  219.     int        dup = 0;
  220.  
  221.     if (year != ANY) {
  222.         year %= 100;
  223.         if (year < 70) year += 100;
  224.         if (year != parse_year)
  225.             return;
  226.     }
  227.     if (month != ANY)
  228.         min_month = max_month = month-1;
  229.     if (month == LAST)
  230.         min_month = max_month = 11;
  231.     if (day != ANY)
  232.         min_day   = max_day   = day;
  233.  
  234.     holiday_name = yacc_string;
  235.     for (m=min_month; m <= max_month; m++)
  236.         if (day == LAST) {
  237.             (void)date_to_time(monthlen[m], m, parse_year,
  238.                                 &wday1, 0, 0);
  239.             nd = (((wday - wday1 + 7) % 7) -
  240.                         ((rel == BEFORE) ? 7 : 0)) % 7;
  241.             for (l=0; l < length; l++)
  242.                 setliteraldate(m,monthlen[m]+nd, off+l, dup++);
  243.         } else
  244.             for (d=min_day; d <= max_day; d++) {
  245.                 (void)date_to_time(d, m, parse_year,
  246.                                 &wday1, 0, 0);
  247.                 nd = (((wday - wday1 + 7) % 7) -
  248.                         ((rel == BEFORE) ? 7 : 0)) % 7;
  249.                 for (l=0; l < length; l++)
  250.                     setliteraldate(m, d+nd, off+l, dup++);
  251.             }
  252. }
  253.  
  254.  
  255. /*
  256.  * set holiday by date. Ignore holidays in the wrong year. The code is
  257.  * complicated by expressions such as "any/last/any" (every last day of
  258.  * the month).
  259.  */
  260.  
  261. setdate(month, day, year, off, length)
  262.     int        month;        /* 1..12, ANY, or LAST */
  263.     int        day;        /* 1..31, ANY, or LAST */
  264.     int        year;        /* 1..2069, or ANY */
  265.     int        off;        /* offset in days */
  266.     int        length;        /* length in days */
  267. {
  268.     int        min_month = 0, max_month = 11;
  269.     int        min_day   = 1, max_day   = 31;
  270.     int        m, d, l;
  271.     int        dup = 0;
  272.  
  273.     if (year != ANY) {
  274.         year %= 100;
  275.         if (year < 70) year += 100;
  276.         if (year != parse_year)
  277.             return;
  278.     }
  279.     if (month != ANY)
  280.         min_month = max_month = month-1;
  281.     if (month == LAST)
  282.         min_month = max_month = 11;
  283.     if (day != ANY)
  284.         min_day   = max_day   = day;
  285.  
  286.     holiday_name = yacc_string;
  287.     for (m=min_month; m <= max_month; m++)
  288.         if (day == LAST)
  289.             for (l=0; l < length; l++)
  290.                 setliteraldate(m, monthlen[m], off+l, dup++);
  291.         else
  292.             for (d=min_day; d <= max_day; d++)
  293.                 for (l=0; l < length; l++)
  294.                     setliteraldate(m, d, off+l, dup++);
  295. }
  296.  
  297.  
  298. /*
  299.  * After the two routines above have removed ambiguities (ANY) and resolved
  300.  * weekday specifications, this routine registers the holiday in the holiday
  301.  * array. There are two of these, for full-line holidays (they take away one
  302.  * appointment line in the month calendar daybox) and "small" holidays, which
  303.  * appear next to the day number. If the day is already some other holiday,
  304.  * ignore the new one. <dup> is information stored for parse_holidays(), it
  305.  * will free() the holiday name only if its dup field is 0 (because many
  306.  * string fields can point to the same string, which was allocated only once
  307.  * by the lexer, and should therefore only be freed once).
  308.  */
  309.  
  310. static colormap[10] = {
  311.     0, COL_HBLACK, COL_HRED, COL_HGREEN, COL_HYELLOW,
  312.     COL_HBLUE, COL_HMAGENTA, COL_HCYAN, COL_HWHITE, COL_WEEKEND };
  313.  
  314. static setliteraldate(month, day, off, dup)
  315.     int        month;        /* 0..11 */
  316.     int        day;        /* 1..31 */
  317.     int        off;        /* offset in days */
  318.     int        dup;        /* flag for later free() */
  319. {
  320.     int julian = JULIAN(month, day) + off;
  321.     struct holiday *hp = yacc_small ? &sm_holiday[julian]
  322.                     : &holiday[julian];
  323.  
  324.     if (julian >= 0 && julian <= 365 && !hp->string) {
  325.         if (!dup)
  326.             holiday_name = mystrdup(holiday_name);
  327.         hp->string    = holiday_name;
  328.         hp->stringcolor    = colormap[yacc_stringcolor];
  329.         hp->daycolor    = colormap[yacc_daycolor];
  330.         hp->dup        = dup;
  331.     }
  332. }
  333.  
  334.  
  335. /*
  336.  * set a holiday relative to Easter
  337.  */
  338.  
  339. seteaster(off, length)
  340.     int        off;        /* offset in days */
  341.     int        length;        /* length in days */
  342. {
  343.     int        dup = 0;    /* flag for later free() */
  344.     int julian = easter_julian + off;
  345.     struct holiday *hp = yacc_small ? &sm_holiday[julian]
  346.                     : &holiday[julian];
  347.  
  348.     holiday_name = yacc_string;
  349.     while (length-- > 0) {
  350.         if (julian >= 0 && julian <= 365 && !hp->string) {
  351.             if (!dup)
  352.                 holiday_name = mystrdup(holiday_name);
  353.             hp->string    = holiday_name;
  354.             hp->stringcolor    = colormap[yacc_stringcolor];
  355.             hp->daycolor    = colormap[yacc_daycolor];
  356.             hp->dup        = dup++;
  357.         }
  358.         julian++, hp++;
  359.     }
  360. }
  361.  
  362.  
  363. /*
  364.  * calculate Easter Sunday as a julian date. I got this from Armin Liebl
  365.  * <liebla@informatik.tu-muenchen.de>, who got it from Knuth. I hope I got
  366.  * all this right...
  367.  */
  368.  
  369. static calc_easter(year)
  370.     int        year;        /* Easter in which year? */
  371. {
  372.     int golden, cent, grcor, clcor, extra, epact, easter;
  373.  
  374.     golden = (year/19)*(-19);
  375.     golden += year+1;
  376.     cent = year/100+1;
  377.     grcor = (cent*3)/(-4)+12;
  378.     clcor = ((cent-18)/(-25)+cent-16)/3;
  379.     extra = (year*5)/4+grcor-10;
  380.     epact = golden*11+20+clcor+grcor;
  381.     epact += (epact/30)*(-30);
  382.     if (epact<=0)
  383.         epact += 30;
  384.     if (epact==25) {
  385.         if (golden>11)
  386.             epact += 1;
  387.     } else {
  388.         if (epact==24)
  389.             epact += 1;
  390.     }
  391.     easter = epact*(-1)+44;
  392.     if (easter<21)
  393.         easter += 30;
  394.     extra += easter;
  395.     extra += (extra/7)*(-7);
  396.     extra *= -1;
  397.     easter += extra+7;
  398.     easter += 31+28+!(year&3)-1;
  399.     return(easter);
  400. }
  401.